// not compiled by itself, included into other files

// [[ex05fc]]
// DEBUG descr The weak reference implementation using the holds to prevent deadlocking.
class weakref;

class reftarget {
	friend class weakref;
public:
	// this pointer will be held in the weak
	// references
	reftarget(void *owner); 
	~reftarget();

	// create a new weak reference to this target
	weakref *newweakref(); 

	// called from the subclass or wrapper class
	// destructor
	void invalidate(); 

protected:
	// notification from a weakref that it gets
	// destroyed
	void freeweakref(weakref *wr); 

	pw::pmutex mutex_; // controls all access
	pw::hold hold_;
	bool valid_; // gets reset on invalidation
	weakref *rlist_; // head of the list of references
	void *owner_; // value to pass to the weak refs

private: 
	// prevent the default and copy constructors and
	// assignments
	reftarget();
	reftarget(const reftarget&);
	void operator=(const reftarget&);
};

class weakref {
	friend class reftarget;
public:
	~weakref();

	// create a copy of this reference
	weakref *copy(); 

	// make the reference strong, return its current value
	void *grab(); 
	// release the reference back to weak
	void release(); 
	
protected:
	// called from copy() and from reftarget
	weakref(reftarget *target); 

	// called from reftarget invalidation
	bool invalidate1(); 
	// called if invalidate1() returned true
	void invalidate2(); 

	// a sort-of-double-linked list, access controlled
	// by target's mutex
	weakref *next_;
	weakref **prevp_;

	pw::pmutex mutex_; // controls all access
	pw::hold hold_; // keeps track of holds from reftarget
	pw::hold grabs_; // keep track of the grabs
	reftarget *target_; // reset to 0 on invalidation

private: 
	// prevent the default and copy constructors and
	// assignments (they may be added but the code is
	// simpler without them)
	weakref();
	weakref(const weakref&);
	void operator=(const weakref&);
};

// ------------------ methods --------------------------

// this pointer will be held in the weak references
reftarget::reftarget(void *owner) : 
	valid_(true), rlist_(0), owner_(owner)
{
}

reftarget::~reftarget()
{
	if (valid_) {
		fprintf(stderr, "~reftarget: not invalidated!\n");
		abort();
	}
}

// create a new weak reference
weakref *reftarget::newweakref() 
{
	weakref *wr;
	wr = new weakref(this);

	pw::lockmutex lm(mutex_);
	if (!valid_) {
		if (wr->invalidate1())
			wr->invalidate2();
	} else { // add to the list
		if (rlist_) {
			rlist_->prevp_ = &wr->next_;
		}
		wr->next_ = rlist_;
		wr->prevp_ = &rlist_;
		rlist_ = wr;
	}
	return wr;
}

// called from the subclass destructor
void reftarget::invalidate() 
{
	pw::lockmutex lm(mutex_);
	valid_ = false;
	while (rlist_) {
		weakref *wr = rlist_;
		rlist_ = rlist_->next_;
		if (rlist_)
			rlist_->prevp_ = &rlist_;
		wr->prevp_ = 0;
		wr->next_ = 0;
		bool code = wr->invalidate1();
		if (code) {
			pw::unlockmutex um(mutex_);
			wr->invalidate2();
		}
	}
	hold_.wait();
}

// notification from a weakref that it gets destroyed
void reftarget::freeweakref(weakref *wr) 
{
	// remove the ref from the list
	pw::lockmutex lm(mutex_);
	if (wr->prevp_) {
		*wr->prevp_ = wr->next_;
		if (wr->next_)
			wr->next_->prevp_ = wr->prevp_;
		wr->prevp_ = 0;
		wr->next_ = 0;
	}
}

weakref::~weakref()
{
	int ngrabs = grabs_.get_hold_count();
	for (int i = 0; i < ngrabs; i++)
		grabs_.release();
	reftarget *t;
	{
		pw::lockmutex lm(mutex_);
		t = target_;
		target_ = 0;
		if (t)
			t->hold_.acquire();
	}
	hold_.wait();
	if (t) {
		t->freeweakref(this);
		t->hold_.release();
	}
}

// create a copy of this reference
weakref *weakref::copy() 
{
	weakref *nwr;
	grab();
	// target won't disappear but the reference may be
	// reset to 0, so make a local copy of it
	reftarget *t = target_;
	if (t)
		nwr = t->newweakref();
	else
		nwr = new weakref(0);
	release();
	return nwr;
}

// make the reference strong, return its current value
void *weakref::grab() 
{
	grabs_.acquire();
	void *owner;
	{
		pw::lockmutex lm(mutex_);
		owner = target_? target_->owner_ : 0;
	}
	return owner;
}

// release the reference back to weak
void weakref::release() 
{ 
	grabs_.release();
}

// called from copy() and from reftarget
weakref::weakref(reftarget *target) : 
	next_(0), prevp_(0), target_(target)
{
}

// called from reftarget invalidation
bool weakref::invalidate1() 
{
	pw::lockmutex lm(mutex_);
	reftarget *t = target_;
	target_ = 0;
	if (t)
		hold_.acquire();
	return (t != 0);
}

// called if invalidate1() returned true
void weakref::invalidate2() 
{
	grabs_.wait();
	hold_.release();
}
// END

